﻿using System;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using Sigil.Impl;

namespace Sigil
{
	public partial class Emit<DelegateType>
	{
		/// <summary>Pushes a pointer to the given function onto the stack, as a native Int32.
		///
		/// To resolve a method at runtime using an Object, use LoadVirtualFunctionPointer instead.
		/// </summary>
		public Emit<DelegateType> LoadFunctionPointer(MethodInfo method)
		{
			if (method == null)
			{
				throw new ArgumentNullException("method");
			}

			var parameters = method.GetParameters();

			var paramList = parameters.Select(p => p.ParameterType);

			var type =
					TypeOnStack.GetKnownFunctionPointer(
							method.CallingConvention,
							HasFlag(method.CallingConvention, CallingConventions.HasThis) ? method.DeclaringType : null,
							method.ReturnType,
							paramList.ToArray()
					);

			UpdateState(OpCodes.Ldftn, method, paramList, Wrap(new[] { new StackTransition(new TypeOnStack[0], new[] { type }) }, "LoadFunctionPointer"));

			return this;
		}
	}
}